package com.howie.parnote.validator.consumer;

import com.howie.parnote.constant.RespMessageConstant;
import com.howie.parnote.validator.annotation.PropertyScriptAssert;
import org.hibernate.validator.internal.util.Contracts;
import org.hibernate.validator.internal.util.logging.Log;
import org.hibernate.validator.internal.util.logging.LoggerFactory;
import org.hibernate.validator.internal.util.scriptengine.ScriptEvaluator;
import org.hibernate.validator.internal.util.scriptengine.ScriptEvaluatorFactory;

import javax.script.ScriptException;
import javax.validation.ConstraintDeclarationException;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

import org.hibernate.validator.internal.util.logging.Messages;

import java.util.HashMap;
import java.util.Map;

/**
 * <p>Title: parnote</p>
 * <p>
 * Description: 脚本验证注解消费类
 * </p>
 *
 * @author huangyan
 *         Created by HY on 2015/2/10 0010.
 * @version v1.0.0
 */
public class PropertyScriptAssertValidator implements ConstraintValidator<PropertyScriptAssert, Object> {

	private static final Log log = LoggerFactory.make();

	private String script;
	private String languageName;
	private String alias;
	private String property;
	private RespMessageConstant message;

	@Override
	public void initialize(PropertyScriptAssert constraintAnnotation) {
		validateParameters(constraintAnnotation);

		this.script = constraintAnnotation.script();
		this.languageName = constraintAnnotation.lang();
		this.alias = constraintAnnotation.alias();
		this.property = constraintAnnotation.property();
		this.message = constraintAnnotation.message();
	}

	private void validateParameters(PropertyScriptAssert constraintAnnotation) {
		Contracts.assertNotEmpty(constraintAnnotation.script(), Messages.MESSAGES.parameterMustNotBeEmpty("script"));
		Contracts.assertNotEmpty(constraintAnnotation.lang(), Messages.MESSAGES.parameterMustNotBeEmpty("lang"));
		Contracts.assertNotEmpty(constraintAnnotation.alias(), Messages.MESSAGES.parameterMustNotBeEmpty("alias"));
		Contracts.assertNotEmpty(constraintAnnotation.property(), Messages.MESSAGES.parameterMustNotBeEmpty("property"));
		Contracts.assertNotEmpty(constraintAnnotation.message().getMessage(), Messages.MESSAGES.parameterMustNotBeEmpty("message"));
	}

	@Override
	public boolean isValid(Object value, ConstraintValidatorContext context) {
		Object evaluationResult;
		ScriptEvaluator scriptEvaluator;

		try {
			ScriptEvaluatorFactory evaluatorFactory = ScriptEvaluatorFactory.getInstance();
			scriptEvaluator = evaluatorFactory.getScriptEvaluatorByLanguageName(languageName);
		} catch (ScriptException e) {
			throw new ConstraintDeclarationException(e);
		}

		try {
			Map<String, Object> bindings = new HashMap<String, Object>();
			bindings.put(alias, value);
			evaluationResult = scriptEvaluator.evaluate(script, bindings);
		} catch (ScriptException e) {
			throw log.getErrorDuringScriptExecutionException(script, e);
		}

		if (evaluationResult == null) {
			throw log.getScriptMustReturnTrueOrFalseException(script);
		}
		if (!(evaluationResult instanceof Boolean)) {
			throw log.getScriptMustReturnTrueOrFalseException(script, evaluationResult, evaluationResult.getClass().getCanonicalName());
		}

		if (Boolean.FALSE.equals(evaluationResult)) {
			context.disableDefaultConstraintViolation();
			context.buildConstraintViolationWithTemplate(message.getMessage()).addPropertyNode(property).addConstraintViolation();
		}

		return Boolean.TRUE.equals(evaluationResult);
	}
}
